package fr.mywiki.view.array;

import java.util.Iterator;
import java.util.List;

import fr.mywiki.model.ejbAdapter.Value;
import fr.mywiki.view.array.cell.CellType;

/**
 * Contains all the necessary information to build a array and each of its
 * pages. The object to include to a formular to manage and display a array.
 */
public class ArrayController {
	/** The way the array is defined */
	private ArrayDefinition arrayDef;

	/** All the elements */
	private List elements;

	/** Current pageNumber number */
	private int pageNumber;

	/** The current page */
	private ArrayPage page;

	private ArrayController() {
	}

	/**
	 * Constructor, initializes the fabric.
	 * 
	 * @param definition
	 *          a <code>ArrayDefinition</code> object.
	 * @param elts
	 *          a <code>List</code> of <code>Value</code> objects.
	 */
	public ArrayController(ArrayDefinition definition, List elts) {
		arrayDef = definition;
		elements = elts;
		setPageNumber(0);
	}

	/**
	 * Builds the desired pageNumber of the array. It does not build the whole
	 * array, only the desired pageNumber. The first pageNumber is numbered 1. No
	 * pageNumber 0.
	 * 
	 * @return a <code>ArrayPage</code> object.
	 */
	protected ArrayPage build() {
		int cols = arrayDef.getColumnNumber();
		List types = arrayDef.getColumnTypes();

		List elts = extractElements(pageNumber);

		ArrayPage arrayPage = new ArrayPage(elts.size(), cols,
				arrayDef.getNoElementMessage());

		// Titles
		arrayPage.setTitles(arrayDef.getColumnTitles());

		// Other cells
		arrayPage.setLines(elts.size());
		Iterator itElts = elts.iterator(); // Which elements to display ?

		int line = 0;
		while (itElts.hasNext()) {
			Value val = (Value) itElts.next();

			// Compute the value to display in each column for this element
			for (int i = 0; i < cols; i++) {
				CellType cellType = (CellType)types.get(i);
				String valStr = cellType.compute(val);
				arrayPage.setCell(line, i, valStr);
			}

			line++;
		}

		return arrayPage;
	}

	/** Extracts the elements of a specific pageNumber */
	private List extractElements(int number) {
		int opp = arrayDef.getObjectsByPage(); // Objects per pageNumber

		int first = opp * number;
		int last = Math.min(elements.size(), opp * (number + 1) - 1);

		return elements.subList(first, last);
	}

	public int getPageNumber() {
		return pageNumber;
	}

	/**
	 * Sets the current pageNumber of the array.
	 * 
	 * @param pageNumber
	 *          the new pageNumber number to affect.
	 * @return false if this pageNumber cannot be reached (not in the limits),
	 *         true otherwise.
	 */
	public boolean setPageNumber(int page) {
		this.pageNumber = page;
		try {
			this.page = build();
		} catch (IndexOutOfBoundsException e) {
			return false;
		}
		return true;
	}

	/**
	 * Increases by one the pageNumber number.
	 * 
	 * @return false if this pageNumber cannot be reached (not in the limits),
	 *         true otherwise.
	 */
	public boolean nextPage() {
		return setPageNumber(pageNumber + 1);
	}

	/**
	 * Decreases of one the pageNumber number.
	 * 
	 * @return false if this pageNumber cannot be reached (not in the limits),
	 *         true otherwise.
	 */
	public boolean previousPage() {
		return setPageNumber(pageNumber - 1);
	}

	/**
	 * Accesses the first pageNumber of the array.
	 */
	public void firstPage() {
		setPageNumber(0);
	}

	/**
	 * Accesses the last pageNumber of the array.
	 */
	public void lastPage() {
		setPageNumber(0);
	}

	/**
	 * Computes and returns the max pageNumber number available.
	 * 
	 * @return an integer.
	 */
	public int getMaxPageNumber() {
		double elts = elements.size();
		double eltsByPage = arrayDef.getObjectsByPage();
		return (int) Math.ceil(elts / eltsByPage);
	}

	public ArrayPage getPage() {
		return page;
	}
}
